/*!	 synfig/rendering/opengl/internal/antialiasing.cpp
**	 Antialiasing
**
**	......... ... 2015 Ivan Mahonin
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#ifndef _WIN32
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#endif

#include <cstring>

#include <synfig/general.h>
#include <synfig/localization.h>

#include "antialiasing.h"

#endif

using namespace synfig;
using namespace rendering;

gl::Antialiasing::Antialiasing(Context &context):
    context(context),
    allowed(false),
    multisample_max_width(),
    multisample_max_height(),
    multisample_texture_id(),
    multisample_renderbuffer_id(),
    multisample_framebuffer_id(),
    multisample_orig_draw_framebuffer_id()
{
    memset(multisample_viewport, 0, sizeof(multisample_viewport));
    memset(multisample_orig_viewport, 0, sizeof(multisample_orig_viewport));

    Context::Lock lock(context);

    // options

    multisample_max_width = 1024;
    multisample_max_height = 1024;
    int multisample_samples = 8;
    bool multisample_hdr = true;
    GLenum multisample_internal_format = multisample_hdr ? GL_RGBA16F : GL_RGBA;

    // multisample

    GLuint prev_draw_framebuffer_id = 0;
    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&prev_draw_framebuffer_id);

    glGenTextures(1, &multisample_texture_id);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, multisample_texture_id);
    glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, multisample_samples, multisample_internal_format, multisample_max_width, multisample_max_height, GL_TRUE);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);

    glGenRenderbuffers(1, &multisample_renderbuffer_id);
    glBindRenderbuffer(GL_RENDERBUFFER, multisample_renderbuffer_id);
    glRenderbufferStorageMultisample(GL_RENDERBUFFER, multisample_samples, GL_STENCIL_INDEX8, multisample_max_width, multisample_max_height);
    glBindRenderbuffer(GL_RENDERBUFFER, 0);

    glGenFramebuffers(1, &multisample_framebuffer_id);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, multisample_framebuffer_id);
    glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, multisample_renderbuffer_id);
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, multisample_texture_id, 0);

    GLenum multisample_status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);

    if (multisample_status != GL_FRAMEBUFFER_COMPLETE) {
        warning("multisample framebuffer incomplete 0x%x", multisample_status);
    } else {
        allowed = true;
    }

    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prev_draw_framebuffer_id);

    context.check();
}

gl::Antialiasing::~Antialiasing()
{
    Context::Lock lock(context);
    glDeleteFramebuffers(1, &multisample_framebuffer_id);
    glDeleteRenderbuffers(1, &multisample_renderbuffer_id);
    glDeleteTextures(1, &multisample_texture_id);
}

void
gl::Antialiasing::multisample_begin(bool clear)
{
    Context::Lock lock(context);

    if (is_allowed()) {
        glGetIntegerv(GL_VIEWPORT, multisample_orig_viewport);
        multisample_viewport[2] = std::min(multisample_orig_viewport[2], multisample_max_width);
        multisample_viewport[3] = std::min(multisample_orig_viewport[3], multisample_max_height);
        glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&multisample_orig_draw_framebuffer_id);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, multisample_framebuffer_id);
        glViewport(
            multisample_viewport[0],
            multisample_viewport[1],
            multisample_viewport[2],
            multisample_viewport[3]);
    }

    if (clear) {
        glClear(GL_COLOR_BUFFER_BIT);
    }
}

void
gl::Antialiasing::multisample_end()
{
    Context::Lock lock(context);

    if (is_allowed()) {
        GLuint prev_read_framebuffer_id = 0;
        glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (GLint*)&prev_read_framebuffer_id);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, (GLuint)multisample_orig_draw_framebuffer_id);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, (GLuint)multisample_framebuffer_id);
        glBlitFramebuffer(
            multisample_viewport[0],
            multisample_viewport[1],
            multisample_viewport[2],
            multisample_viewport[3],
            multisample_orig_viewport[0],
            multisample_orig_viewport[1],
            multisample_orig_viewport[2],
            multisample_orig_viewport[3],
            GL_COLOR_BUFFER_BIT, GL_LINEAR);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, prev_read_framebuffer_id);
        glViewport(
            multisample_orig_viewport[0],
            multisample_orig_viewport[1],
            multisample_orig_viewport[2],
            multisample_orig_viewport[3]);
    }
}